#include <sys/time.h>
#include <iostream>
#include <iomanip>
#include <sys/times.h>

#include "netdec.hpp"

using namespace std;

void
test (int argc, char *argv[]){
  Netdec dec;
  NDecResult * res;
  NDecFilter * f[4], *ipv4Src, *ipv4Dst;
  unsigned long long int d = 0;

  const unsigned long long int pktBlockSize = 1000000;
  Preference pref;
  bool verbose = false;

  pref.define ("r",         "", false, PrefDef::PT_STR,  "", "Pcap foramt file");
  pref.define ("i",         "", false, PrefDef::PT_STR,  "", "Network Interface");
  pref.define ("v",         "", false, PrefDef::PT_BOOL, "", "Verbose display");
  pref.define ("ipv4.addr", "", false, PrefDef::PT_STR,  "", "IPv4 address filter");
  pref.define ("tcp.port",  "", false, PrefDef::PT_INT,  "", "TCP port filter");
  if (! pref.import (argc, argv))
    {
      cerr << "ERROR: " << pref.getError () << endl;
      return ;
    }

  memset (f, 0, sizeof (f));

  int i;
  assert (ipv4Src = dec.makeFilter ("ipv4.src_addr", ""));
  assert (ipv4Dst = dec.makeFilter ("ipv4.dst_addr", ""));

  if (0 < (i = pref.lookup ("ipv4.addr")))
    {
      assert (NULL != (f[0] = dec.makeFilter ("ipv4.src_addr", pref.getStr ("ipv4.addr", 0))));
      assert (NULL != (f[1] = dec.makeFilter ("ipv4.dst_addr", pref.getStr ("ipv4.addr", 0))));
    }

  if (0 < (i = pref.lookup ("tcp.port")))
    {
      assert (NULL != (f[2] = dec.makeFilter ("tcp.src_port", pref.getStr ("tcp.port", 0))));
      assert (NULL != (f[3] = dec.makeFilter ("tcp.dst_port", pref.getStr ("tcp.port", 0))));
    }

  verbose = pref.getBool ("v", 0);

  if (0 < pref.lookup ("r"))
    {
      if (false == dec.setupFile (pref.getStr ("r", 0)))
	{
	  cerr << "Test data read error.." << endl;
	  return ;	  
	}
    }
  else if (0 < pref.lookup ("i"))
    {
      if (false == dec.setupDevice (pref.getStr ("i", 0)))
	{
	  cerr << "Test data read error.." << endl;
	  return ;	  
	}
    }
  else
    {
      cerr << "You have to specify test pcap data by '-r' option." << endl;
      return ;
    }


  PTreeNode<NDecBox> testNDecBox ;
  PTreeNode<Node> testNode ;
  PTreeNode<Session> testSession ;
  PTreeNode<TcpSession> testTcpSession ;
  PTreeNode<TimerUnit> testTimerUnit ;
  PTreeNode<UdpSession> testUdpSession ;


  while (NULL != (res = dec.fetch ()))
    {
      d++;
      bool detect = false;

      if (f[0] && f[0]->exec (*res))
	{
	  if (verbose)
	    printf (".");

	  detect = true;
	}

      if (f[1] && f[1]->exec (*res))
	{
	  if (verbose)
	    printf (".");

	  detect = true;
	}


      if ((f[2] && f[2]->exec (*res)) || (f[3] && f[3]->exec (*res)))
	{
	  if (verbose)
	    printf ("[match TCP port] ");

	  detect = true;
	}

      
      
      if (detect)
	{
	  Node * src, * dst;
	  NDecBox * sv = NULL, * dv = NULL;
	  boxkey_t key = 1;
	  bool var = true, * bp;
	  bool infect = false;
	  int varTTL = 100;
	  bool display = false;

	  if (NULL != (src = res->getSrcNode ()))
	    sv = src->fetchBox (key, varTTL);
	  if (NULL != (dst = res->getDstNode ()))
	    dv = dst->fetchBox (key, varTTL);
	  
	  assert (sv && dv);
	  if (NULL == (bp = static_cast<bool*>(sv->getPtr ())))
	    {
	      sv->setData (static_cast<void*>(&var), sizeof (bool), VT_DATA);
	      printf ("[new src infected] ");
	      infect = true;
	      display = true;
	    }


	  if (NULL == (bp = static_cast<bool*>(dv->getPtr ())))
	    {
	      dv->setData (static_cast<void*>(&var), sizeof (bool), VT_DATA);
	      printf ("[new dst infected] ");
	      infect = true;
	      display = true;
	    }


	  if (infect && ipv4Src->exec (*res) && ipv4Dst->exec (*res))
	    {
	      char srcAddrBuf[BUFSIZ], dstAddrBuf[BUFSIZ];
	      inet_ntop (AF_INET, ipv4Src->getResult (NULL), srcAddrBuf, sizeof (srcAddrBuf));
	      inet_ntop (AF_INET, ipv4Dst->getResult (NULL), dstAddrBuf, sizeof (dstAddrBuf));	      
	      printf ("%s -> %s ", srcAddrBuf, dstAddrBuf);
	    }

	  if (display)
	    printf ("\n");

	  fflush (stdout);
	}
      
      if (d % pktBlockSize == 0)
	{
  
	  dec.beat ();
	  printf ("[%llu pkt] %3.6f   %10.3f pps / %4.3f Mbps  session =%8d, node =%8d\n", 
		  d, dec.getDeltaSpan (), dec.getDeltaPPS (), dec.getDeltaBPS () / 1000000,
		  PTreeNode<Session>::getNodeCount (), PTreeNode<Node>::getNodeCount());
	}
    }


  for (size_t i = 0; i < sizeof (f) / sizeof (NDecFilter*); i++)
    delete f[i];

  delete ipv4Src;
  delete ipv4Dst;

  return ;
}

int
main (int argc, char *argv[])
{
  cout << "TEST ===== start ========" << endl;
  test (argc, argv);
  cout << "TEST ===== exit =========" << endl;

  return 0;
}
